
package de.dopichaj.labrador.demo;


import java.io.BufferedReader;
import java.io.File;
import java.io.InputStreamReader;
import java.util.Iterator;
import java.util.List;

import org.apache.commons.cli.BasicParser;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.apache.commons.lang.StringEscapeUtils;

import de.dopichaj.labrador.index.own.OwnFileSystemIndex;
import de.dopichaj.labrador.search.XMLSearcher;
import de.dopichaj.labrador.search.hit.Hit;
import de.dopichaj.labrador.search.hit.HitComparator;
import de.dopichaj.labrador.search.merge.pattern.AbstractPattern;
import de.dopichaj.labrador.search.merge.pattern.PatternMerge;
import de.dopichaj.labrador.search.prune.ContextPruner;
import de.dopichaj.labrador.search.prune.FuzzyLengthBasedPruner;
import de.dopichaj.labrador.search.prune.ResultTreePruner;

/**
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * This is based on a copy of the lucene demo for searching files. It has been
 * modified to serve as a front-end for xml searching. <br />
 * @author Benedikt
 */
public class SearchFiles {

    /**
     * The index-directory used if none is specified in constructor or config-file.
     */
    private static final String DEFAULT_INDEX_DIRECTORY = "index";

    /**
     * The main class for searching files.
     */
    public static void main(String[] args) throws Exception {
        Options options = new Options();
        options.addOption("x", "xmloutput", false, "Wrap output in XML");
        options.addOption("n", "hitsperpage", true, "Hits per page");
        options.addOption("p", "patterns", true, "Use given patterns (class names separated by commas)");
        options.addOption("i", "de.dopichaj.labrador.index", true, "Index directory");
        options.addOption("I", "interactive", false, "Interactive mode");
        options.addOption("X", "sortbyxpath", false, "Sort results by XPath");
        options.addOption("f", "onlyfilename", false, "Omit path in result list");
        options.addOption("P", "pathrelative", true, "Output path relative to argument");

        CommandLineParser parser = new BasicParser();
        CommandLine cmd = null;
        try {
            cmd = parser.parse(options, args);
        } catch (ParseException pe) {
            help(options, "");
        }

        boolean sortByXPath = cmd.hasOption('X');
        boolean onlyFilename = cmd.hasOption('f');
        boolean interactiveMode = cmd.hasOption('I');

        int hitsPerPage = 10;
        try {
            hitsPerPage = Integer.parseInt(cmd.getOptionValue('n', "10"));
        } catch (NumberFormatException e) {
            help(options, "Argument to -n must be numeric");
        }

        String pathRelativeTo = cmd.getOptionValue('P', "");
        File indexDirectory = new File(cmd.getOptionValue('i', DEFAULT_INDEX_DIRECTORY));

        BufferedReader in = new BufferedReader(new InputStreamReader(System.in));

        ResultTreePruner pruner;
        if (cmd.hasOption('p')) {
            String[] patternNames = cmd.getOptionValue('p').split(",");
            AbstractPattern[] patterns = new AbstractPattern[patternNames.length];
            for (int i = 0; i < patternNames.length; i++) {
                String patternName = patternNames[i];
                Class patternClass = Class.forName(patternName);
                AbstractPattern pattern = (AbstractPattern)patternClass.newInstance();
                patterns[i] = pattern;
            }
            PatternMerge merge = new PatternMerge(patterns);
            pruner = new ContextPruner(merge);
        } else {
            pruner = new FuzzyLengthBasedPruner();
        }

        XMLSearcher searcher = new XMLSearcher(
            new OwnFileSystemIndex(indexDirectory).getNEXISearcher(), pruner);

        print("NEXI query: ");
        String query = in.readLine();
        List<Hit> results = null;
        if (sortByXPath) {
            results = searcher.search(query,
                new HitComparator(HitComparator.SORT_BY_XPATH));
        } else {
            results = searcher.search(query);
        }

        printXMLWrapping("<?xml version='1.0'?>");
        printXMLWrapping("<topic>");

        Iterator<Hit> it = results.iterator();
        int i = 1;
        double maxScore = 1;
        while (it.hasNext()) {
            do {
                Hit hit = it.next();
                if (i == 1) {
                    maxScore = hit.getScore();
                }
                File file = hit.getFile();
                String xPath = hit.getXPath();
                int normalizedScore = (int)(hit.getScore() * 100 / maxScore);
                int length = hit.getContentLength();

                if (file != null) {
                    println(i
                            + ". - "
                            + normalizedScore
                            + "% - ("
                            + hit.getScore()
                            + ") - "
                            + ((onlyFilename) ? file.getName() : file.toString()
                                    .substring(pathRelativeTo.length()))
                            + " - " + xPath + " - " + length);

                    printXMLWrapping("    <result>");

                    printXMLWrapping("        <file>"
                        + StringEscapeUtils.escapeXml(
                            file.toString().substring(pathRelativeTo.length(), file.toString().length() - 4)
                        ) +
                        "</file>");
                    printXMLWrapping("        <path>" + xPath + "</path>");
                    printXMLWrapping("        <rsv>" + hit.getScore()
                            + "</rsv>");

                    printXMLWrapping("    </result>");

                } else {
                    println(i + ". " + "No path for this document");
                }
                i++;
            } while (it.hasNext() && (i % hitsPerPage) != 0);

            if (!interactiveMode)
                break;

            if (it.hasNext()) {
                print("Display more (y/n) ? ");
                String line = in.readLine();
                if (line == null || line.length() == 0 || line.charAt(0) == 'n')
                    break;
            }
        }
        printXMLWrapping("</topic>");
        searcher.close();
    }

    private static boolean wrapOutputInXML = false;

    /**
     * Only output if -x is specified.
     * @param xml Text to print.
     */
    private static void printXMLWrapping(String xml) {
        if (wrapOutputInXML)
            System.out.println(xml);
    }

    /**
     * Only output if -x is <strong>not</strong> specified.
     * Adds a newline.
     * @param text Text to print.
     */
    private static void println(String text) {
        if (!wrapOutputInXML) {
            System.out.println(text);
        }
    }

    /**
     * Only output if -x is <strong>not</strong> specified.
     * @param text Text to print.
     */
    private static void print(String text) {
        if (!wrapOutputInXML) {
            System.out.print(text);
            System.out.flush();
        }
    }

    private static void help(Options options, String error) {
        HelpFormatter fmt = new HelpFormatter();
        fmt.printHelp("java SearchFiles", "", options, "\n" + error, true);
        System.exit(1);
    }
}
